/**
 * Copyright (c) 2011 Roundarch (www.roundarch.com)
 *
 * Author Joshua Jung
 *
 * ---
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.roundarch.flex.controls.graphControlClasses
{
    import com.roundarch.flex.controls.GraphControlBase;
    import com.roundarch.flex.controls.graphControlInterfaces.IGraphControlRenderer;
    import com.roundarch.flex.controls.graphControlInterfaces.IInterval;
    import com.roundarch.flex.controls.graphControlInterfaces.IIntervalCollection;

    import flash.events.Event;
    import flash.geom.Point;

    import mx.core.UIComponent;
    import mx.messaging.AbstractConsumer;
    import mx.styles.CSSStyleDeclaration;
    import mx.styles.StyleManager;

    //-----------------------------
    //  Styles
    //-----------------------------
    /**
     *  The tick color
     *
     *  @default 0x666666
     */
    [Style(name = "tickColor", type = "uint", format = "Color", inherit = "no")]
    /**
     *  The tick thickness
     *
     *  @default 2
     */
    [Style(name = "tickThickness", type = "Number", format = "Length", inherit = "no")]
    /**
     *  The tick height
     *
     *  @default 10
     */
    [Style(name = "tickHeight", type = "Object", format = "Length", inherit = "no")]

    /**
     * @author jjung
     */
    public class GraphControlTickRenderer extends UIComponent implements IGraphControlRenderer
    {
        //---------------------------------------------------------------------
        //
        //  Variables
        //
        //---------------------------------------------------------------------

        //---------------------------------------------------------------------
        //
        //  Properties
        //
        //---------------------------------------------------------------------
        //-----------------------------
        // direction
        //-----------------------------
        /**
         * @private
         */
        protected var _direction : String = "horizontal";

        [Bindable("directionChanged")]
        /**
         * The direction this tick renderer is moving. Can be "horizontal" or "vertical".
         *
         * @default "horizontal"
         */
        public function get direction() : String
        {
            return _direction;
        }

        /**
         * @private
         */
        public function set direction(value : String) : void
        {
            if (value == _direction)
            {
                return;
            }

            _direction = value;

            dispatchEvent(new Event("directionChanged"));
        }

        //-----------------------------
        // intervalCollection
        //-----------------------------
        /**
         * @private
         */
        protected var _intervalCollection : IIntervalCollection = null;

        [Bindable("intervalCollectionChanged")]
        /**
         *
         *
         * @default null
         */
        public function get intervalCollection() : IIntervalCollection
        {
            return _intervalCollection;
        }

        /**
         * @private
         */
        public function set intervalCollection(value : IIntervalCollection) : void
        {
            if (value == _intervalCollection)
            {
                return;
            }

            _intervalCollection = value;

            dispatchEvent(new Event("intervalCollectionChanged"));
        }

        //-----------------------------
        // minTicks
        //-----------------------------
        /**
         * @private
         */
        protected var _minTicks : int = 1;

        [Bindable("minTicksChanged")]
        /**
         * The minimum number of ticks that will be displayed. If the number falls below this, no ticks are displayed at all.
         *
         * @default 2
         */
        public function get minTicks() : int
        {
            return _minTicks;
        }

        /**
         * @private
         */
        public function set minTicks(value : int) : void
        {
            if (value == _minTicks)
            {
                return;
            }

            _minTicks = value;

            invalidateDisplayList();

            dispatchEvent(new Event("minTicksChanged"));
        }

        //-----------------------------
        // tickCutoffDistance
        //-----------------------------
        /**
         * @private
         */
        protected var _tickCutoffDistance : Number = 1.9;

        [Bindable("tickCutoffDistanceChanged")]
        /**
         * If tick marks are closer together than this value, nothing is drawn at all.
         *
         * @default 1.9
         */
        public function get tickCutoffDistance() : Number
        {
            return _tickCutoffDistance;
        }

        /**
         * @private
         */
        public function set tickCutoffDistance(value : Number) : void
        {
            if (value == _tickCutoffDistance)
            {
                return;
            }

            _tickCutoffDistance = value;

            invalidateDisplayList();

            dispatchEvent(new Event("tickCutoffDistanceChanged"));
        }

        //-----------------------------
        // parentGC - IGraphControlChild
        //-----------------------------
        /**
         * @private
         */
        private var _parentGC : GraphControlBase;

        /**
         * @private
         */
        public function get parentGC() : GraphControlBase
        {
            return _parentGC;
        }

        /**
         * @inheritDoc
         */
        public function set parentGC(value : GraphControlBase) : void
        {
            _parentGC = value;
        }

        //---------------------------------------------------------------------
        //
        //  Constructor
        //
        //---------------------------------------------------------------------
        /**
         *  Constructor
         */
        public function GraphControlTickRenderer()
        {
            super();
        }

        //---------------------------------------------------------------------
        //
        //  Methods
        //
        //---------------------------------------------------------------------
        /**
         *  @private
         *  Initializes the default styles for 'GraphControlTickRenderer'.
         */
        private static function initDefaultStyles() : void
        {
            var styles : CSSStyleDeclaration = StyleManager.getStyleDeclaration("GraphControlTickRenderer");

            if (!styles)
            {
                styles = new CSSStyleDeclaration();
            }

            if (styles.defaultFactory == null)
            {
                styles.defaultFactory = function() : void
                {
                    this.tickColor = 0x666666;
                    this.tickThickness = 2;
                    this.tickHeight = 10;
                }
            }

            StyleManager.setStyleDeclaration("GraphControlTickRenderer", styles, false);
        }

        /**
         * @private
         */
        override public function getStyle(styleProp : String) : *
        {
            if (styleProp == "tickHeight")
            {
                var style : Object = super.getStyle("tickHeight");

                if (style is String)
                {
                    var percent : Number = parseInt(super.getStyle("tickHeight")) / 100.0;

                    return parent.height * percent;
                }
            }

            return super.getStyle(styleProp);
        }

        /**
         * @private
         * Initialize the default styles
         */
        initDefaultStyles();

        //---------------------------------------------------------------------
        //
        //  Overridden Methods
        //
        //---------------------------------------------------------------------
        /**
         * @inheritDoc
         */
        override protected function updateDisplayList(unscaledWidth : Number, unscaledHeight : Number) : void
        {
            super.updateDisplayList(unscaledWidth, unscaledHeight);

            graphics.clear();

            if (!intervalCollection)
            {
                return;
            }

            if (direction == "horizontal")
            {
                var start : Number = Math.max(parentGC.horizontalAxisOptions.visibleMinMax.start, parentGC.horizontalAxisOptions.minMax.start);
                var end : Number = Math.min(parentGC.horizontalAxisOptions.visibleMinMax.end, parentGC.horizontalAxisOptions.minMax.end);

                // If the user "scrolls" off the end we might end up with the start being after or equal to the end so
                // we have to fix that.
                if (start >= end)
                {
                    start = end - 1;
                }

                var intervals : Array = intervalCollection.getIntervalsFor(start, end);

                if (intervals.length && intervals.length > minTicks && GraphControlUtil.getPixelWidthForInterval(intervals[0] as IInterval, parentGC) >= tickCutoffDistance)
                {
                    graphics.lineStyle(getStyle("tickThickness"), getStyle("tickColor"));

                    for each (var interval : IInterval in intervals)
                    {
                        if (interval.start > parentGC.horizontalAxisOptions.visibleMinMax.start && interval.start < parentGC.horizontalAxisOptions.visibleMinMax.end)
                        {
                            var xPos : Number = GraphControlUtil.getScreenforValue(new Point(interval.start, 0), parentGC).x;

                            graphics.moveTo(xPos, parentGC.height);

                            graphics.lineTo(xPos, parentGC.height - getStyle("tickHeight"));
                        }
                    }
                }
            }
            else if (direction == "vertical")
            {
                var intervals : Array = intervalCollection.getIntervalsFor(parentGC.verticalAxisOptions.visibleMinMax.start, parentGC.verticalAxisOptions.visibleMinMax.end);

                if (intervals.length && intervals.length > minTicks && GraphControlUtil.getPixelHeightForInterval(intervals[0] as IInterval, parentGC) >= tickCutoffDistance)
                {
                    graphics.lineStyle(getStyle("tickThickness"), getStyle("tickColor"));

                    for each (var interval : IInterval in intervals)
                    {
                        if (interval.start > parentGC.horizontalAxisOptions.visibleMinMax.start && interval.start < parentGC.horizontalAxisOptions.visibleMinMax.end)
                        {
                            var yPos : Number = GraphControlUtil.getScreenforValue(new Point(interval.start, 0), parentGC).y;

                            graphics.moveTo(yPos, 0);
                            graphics.lineTo(yPos, getStyle("tickWidth"));
                        }
                    }
                }
            }
        }

        //---------------------------------------------------------------------
        //
        //  Events
        //
        //---------------------------------------------------------------------
    }
}
